package com.xuecheng.managecms.service;

import com.alibaba.fastjson.JSONObject;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.xuecheng.framework.domain.cms.CmsPage;
import com.xuecheng.framework.domain.cms.CmsSite;
import com.xuecheng.framework.domain.cms.CmsTemplate;
import com.xuecheng.framework.domain.cms.request.QueryPageRequest;
import com.xuecheng.framework.domain.cms.response.CmsCode;
import com.xuecheng.framework.domain.cms.response.CmsPageResult;
import com.xuecheng.framework.domain.cms.response.CmsPostPageResult;
import com.xuecheng.framework.exception.CustomException;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.QueryResponseResult;
import com.xuecheng.framework.model.response.QueryResult;
import com.xuecheng.framework.model.response.ResponseResult;
import com.xuecheng.managecms.config.RabbitMQConfig;
import com.xuecheng.managecms.dao.CmsPageRepository;
import com.xuecheng.managecms.dao.CmsSiteRepository;
import com.xuecheng.managecms.dao.CmsTemplateRepository;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Optional;

/**
 * @author jl
 * @description
 * @date 2019-08-06 17:25
 */
@Service
public class CmsPageService {
    @Autowired
    private CmsPageRepository cmsPageRepository;
    @Autowired
    private CmsTemplateRepository cmsTemplateRepository;
    @Autowired
    private CmsSiteRepository cmsSiteRepository;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private GridFsTemplate gridFsTemplate;
    @Autowired
    private GridFSBucket gridFSBucket;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * @param page 定义前端第一页传1
     * @param size
     * @param queryPageRequest
     * @return
     */
    public QueryResponseResult findList(int page, int size, QueryPageRequest queryPageRequest) {
        if (queryPageRequest == null) {
            queryPageRequest = new QueryPageRequest();
        }
        CmsPage cmsPage = new CmsPage();
        if (StringUtils.isNotEmpty(queryPageRequest.getSiteId())) {
            cmsPage.setSiteId(queryPageRequest.getSiteId());
        }
        if (StringUtils.isNotEmpty(queryPageRequest.getTemplateId())) {
            cmsPage.setTemplateId(queryPageRequest.getTemplateId());
        }
        if (StringUtils.isNotEmpty(queryPageRequest.getPageAliase())) {
            cmsPage.setPageAliase(queryPageRequest.getPageAliase());
        }
        if (StringUtils.isNotEmpty(queryPageRequest.getPageId())) {
            cmsPage.setPageId(queryPageRequest.getPageId());
        }
        if (StringUtils.isNotEmpty(queryPageRequest.getPageName())) {
            cmsPage.setPageName(queryPageRequest.getPageName());
        }
        //条件匹配器 设置pageAliase,pageName模糊查询
        /*
        * ExampleMatcher.GenericPropertyMatchers.contains() 包含关键字
        * ExampleMatcher.GenericPropertyMatchers.exact() 精确匹配
        * ExampleMatcher.GenericPropertyMatchers.caseSensitive() 区分大小写
        * ExampleMatcher.GenericPropertyMatchers.startsWith() 以什么开头
        * */
        ExampleMatcher matching = ExampleMatcher.matching().withMatcher("pageAliase", ExampleMatcher.GenericPropertyMatchers.contains()).withMatcher("pageName", ExampleMatcher.GenericPropertyMatchers.contains());
        //参数1:条件值对象,精确查询   参数2:条件匹配器
        Example<CmsPage> example = Example.of(cmsPage, matching);

        if (page <= 0) {
            page = 1;
        }
        //数据库第一条是从0开始,减1之后传给dao
        page -= 1;
        if (size <= 0) {
            size = 10;
        }
        PageRequest pageRequest = PageRequest.of(page, size);
        //jpa的分页
        Page<CmsPage> pages = cmsPageRepository.findAll(example, pageRequest);
        QueryResult<CmsPage> queryResult = new QueryResult<>(pages.getTotalElements(), pages.getContent());
        return new QueryResponseResult(CommonCode.SUCCESS, queryResult);
    }

    public CmsPageResult add(CmsPage cmsPage) {
        //参数校验
        if (cmsPage == null) {
            throw new CustomException(CommonCode.INVALID_PARAM);
        }
        //根据页面名称,站点id,页面webpath校验唯一性
        //mongo中collection表cms_page中有这3个字段的唯一索引
        CmsPage result = cmsPageRepository.findByPageNameAndSiteIdAndPageWebPath(cmsPage.getPageName(), cmsPage.getSiteId(), cmsPage.getPageWebPath());
        if (result != null) {
            //页面已经存在,添加失败
            throw new CustomException(CmsCode.CMS_ADDPAGE_EXISTSNAME);
        }
        cmsPage.setPageId(null);
        cmsPage.setPageCreateTime(LocalDateTime.now());
        cmsPageRepository.save(cmsPage);
        return new CmsPageResult(CommonCode.SUCCESS, cmsPage);
    }

    /**
     * 根据id查询页面
     *
     * @param id
     * @return
     */
    public CmsPage getById(String id) {
        Optional<CmsPage> optional = cmsPageRepository.findById(id);
        return optional.orElse(null);
    }

    /**
     * 更新页面信息
     *
     * @param id
     * @param cmsPage
     * @return
     */
    public CmsPageResult update(String id, CmsPage cmsPage) {
        //根据id查询页面信息
        CmsPage one = this.getById(id);
        if (one != null) {
            //更新模板id
            one.setTemplateId(cmsPage.getTemplateId());
            //更新所属站点
            one.setSiteId(cmsPage.getSiteId());
            //更新页面别名
            one.setPageAliase(cmsPage.getPageAliase());
            //更新页面名称
            one.setPageName(cmsPage.getPageName());
            //更新访问路径
            one.setPageWebPath(cmsPage.getPageWebPath());
            //更新物理路径
            one.setPagePhysicalPath(cmsPage.getPagePhysicalPath());
            //更新dataUrl
            one.setDataUrl(cmsPage.getDataUrl());
            //执行更新
            CmsPage save = cmsPageRepository.save(one);
            //返回成功
            return new CmsPageResult(CommonCode.SUCCESS, save);
        }
        //返回失败
        return new CmsPageResult(CommonCode.FAIL, null);
    }

    public ResponseResult delete(String id) {
        //查询
        Optional<CmsPage> optional = cmsPageRepository.findById(id);
        if (optional.isPresent()) {
            cmsPageRepository.deleteById(id);
            return new ResponseResult(CommonCode.SUCCESS);
        }
        return new ResponseResult(CommonCode.FAIL);
    }

    /**
     * 页面发布
     */
    public ResponseResult post(String pageId) {
        //执行页面静态化
        String pageHtml = getPageHtml(pageId);
        //将静态化文件存储到GridFS中
        CmsPage cmsPage = saveHtml(pageHtml, pageId);
        //向MQ发消息
        sendPostPage(cmsPage);
        return ResponseResult.SUCCESS();
    }

    /**
     * 向MQ发消息
     */
    private void sendPostPage(CmsPage cmsPage) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("pageId",cmsPage.getPageId());
        String jsonString = jsonObject.toJSONString();
        rabbitTemplate.convertAndSend(RabbitMQConfig.EX_ROUTING_CMS_POSTPAGE,cmsPage.getSiteId(),jsonString);
    }

    /**
     * 保存html到GridFS
     */
    private CmsPage saveHtml(String pageHtml, String pageId) {
        CmsPage cmsPage = getById(pageId);
        if (cmsPage == null) {
            throw new CustomException(CommonCode.INVALID_PARAM);
        }
        ObjectId objectId = null;
        InputStream inputStream = null;
        try {
            //将pageHtml转换成输入流
            inputStream = IOUtils.toInputStream(pageHtml, "utf-8");
            //保存到GridFS
            objectId = gridFsTemplate.store(inputStream, cmsPage.getPageName());

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                assert inputStream != null;
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //将html文件id更新到cmsPage中
        cmsPage.setHtmlFileId(String.valueOf(objectId));
        cmsPageRepository.save(cmsPage);
        return cmsPage;
    }

    /**
     * 页面静态化方法
     *
     * @param pageId
     * @return
     */
    public String getPageHtml(String pageId) {
        //获取数据
        Map model = getModelByPageId(pageId);
        if (model == null) {
            throw new CustomException(CmsCode.CMS_GENERATEHTML_DATAISNULL);
        }
        //获取模板
        String template = getTemplateByPageId(pageId);
        if (StringUtils.isEmpty(template)) {
            throw new CustomException(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
        }
        //执行静态化
        return generateHtml(template, model);
    }

    /**
     * 获取模型数据
     *
     * @param pageId
     * @return
     */
    private Map getModelByPageId(String pageId) {
        CmsPage cmsPage = this.getById(pageId);
        if (cmsPage == null) {
            throw new CustomException(CmsCode.CMS_PAGE_NOTEXISTS);
        }
        //获取dataUrl
        String dataUrl = cmsPage.getDataUrl();
        if (StringUtils.isEmpty(dataUrl)) {
            throw new CustomException(CmsCode.CMS_GENERATEHTML_DATAURLISNULL);
        }
        //通过restTemplate请求dataUrl获取数据
        ResponseEntity<Map> entity = restTemplate.getForEntity(dataUrl, Map.class);
        return entity.getBody();
    }

    /**
     * 获取页面模板
     *
     * @param pageId
     * @return
     */
    private String getTemplateByPageId(String pageId) {
        CmsPage cmsPage = this.getById(pageId);
        if (cmsPage == null) {
            throw new CustomException(CmsCode.CMS_PAGE_NOTEXISTS);
        }
        String templateId = cmsPage.getTemplateId();
        if (StringUtils.isEmpty(templateId)) {
            throw new CustomException(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
        }
        //查询模板信息
        Optional<CmsTemplate> optional = cmsTemplateRepository.findById(templateId);
        if (optional.isPresent()) {
            CmsTemplate cmsTemplate = optional.get();
            //获取模板文件id
            String templateFileId = cmsTemplate.getTemplateFileId();
            //通过GridFS查询模板文件
            GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(templateFileId)));
            if (gridFSFile == null) {
                throw new CustomException(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
            }
            //打开下载流
            GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
            //创建GridFsResource对象,获取流
            GridFsResource gridFsResource = new GridFsResource(gridFSFile, gridFSDownloadStream);
            //从流中获取数据
            try {
                String content = IOUtils.toString(gridFsResource.getInputStream(), "utf-8");
                return content;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 执行静态化
     */
    private String generateHtml(String template, Map model) {
        //创建配置对象
        Configuration configuration = new Configuration(Configuration.getVersion());
        //创建模板加载器
        StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
        stringTemplateLoader.putTemplate("template",template);
        //向configuration配置模板加载器
        configuration.setTemplateLoader(stringTemplateLoader);
        //获取模板
        try {
            Template template1 = configuration.getTemplate("template");
            //调用api进行静态化
            return FreeMarkerTemplateUtils.processTemplateIntoString(template1,model);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 添加页面，如果已存在则更新页面
     * @param cmsPage
     * @return
     */
    public CmsPageResult save(CmsPage cmsPage){
        //校验页面是否存在，根据页面名称、站点Id、页面webpath查询
        CmsPage one = cmsPageRepository.findByPageNameAndSiteIdAndPageWebPath(cmsPage.getPageName(),cmsPage.getSiteId(), cmsPage.getPageWebPath());
        if(one !=null){
            //更新
            return this.update(one.getPageId(),cmsPage);
        }else{
            //添加
            return this.add(cmsPage);
        }
    }

    public CmsPostPageResult postPageQuick(CmsPage cmsPage) {
        //将页面信息存储到cms_page中
        CmsPageResult save = this.save(cmsPage);
        if (!save.isSuccess()) {
            throw new CustomException(CommonCode.FAIL);
        }
        //得到页面id
        String pageId = save.getCmsPage().getPageId();
        //执行页面发布(先静态化、保存到GridFS,向MQ发消息)
        ResponseResult post = this.post(pageId);
        if (!post.isSuccess()) {
            throw new CustomException(CommonCode.FAIL);
        }
        //拼装pageUrl
        CmsPage saveCmsPage = save.getCmsPage();
        String siteId = saveCmsPage.getSiteId();
        CmsSite cmsSite = findCmsSiteById(siteId);
        String pageUrl = cmsSite.getSiteDomain() + cmsSite.getSitePhysicalPath() + saveCmsPage.getPageWebPath() + saveCmsPage.getPageName();
        return new CmsPostPageResult(CommonCode.SUCCESS,pageUrl);
    }

    //根据站点id查询站点信息
    public CmsSite findCmsSiteById(String siteId) {
        Optional<CmsSite> optional = cmsSiteRepository.findById(siteId);
        return optional.orElse(null);
    }
}
